Update Gradle cache with the configurable plugin support#114
Update Gradle cache with the configurable plugin support#114matikepa wants to merge 6 commits intogit-pkgs:mainfrom
Conversation
andrew
left a comment
There was a problem hiding this comment.
Thanks for the follow-up, the marker detection logic is correct and the config wiring matches the existing upstream pattern. A few things before merging:
Checksums and metadata don't fall back. .sha1/.sha256/.md5 and maven-metadata.xml are routed through isMetadataFile to h.upstreamURL only, with no Plugin Portal fallback. I checked com.diffplug.spotless.gradle.plugin-8.4.0.pom.sha1: 404 on Central, 200 on the Plugin Portal. So the POM resolves but its checksum doesn't, which breaks builds with verification-metadata.xml enabled. The metadata path needs the same fallback treatment for marker coordinates.
Duplicate tests. TestMavenHandler_GradlePluginMarkerFallbackAndCache and the _BenManes variant are identical apart from the path string. Please collapse them into one table-driven test.
Minor: the h.pluginPortalUpstreamURL == "" check in shouldFallbackToPluginPortal is unreachable since the constructor always sets a default. Fine to drop it.
One thing to flag for a follow-up rather than this PR: the fallback only covers *.gradle.plugin marker artifacts. Once Gradle reads the marker it fetches the implementation jar (e.g. com.diffplug.spotless:spotless-plugin-gradle), which won't match the suffix and goes to Central only. Spotless publishes to both so it works, but Portal-only plugins will still fail at that step. The README now suggests pluginManagement { maven(...) } is fully handled, so worth either a caveat in the docs or a broader fallback later.
andrew
left a comment
There was a problem hiding this comment.
Overall the config wiring (config.go, env vars, docs, server.go) is clean and matches the existing npm/cargo upstream pattern, .module support is correct, and the fallback test coverage is thorough including the negative case and checksum sidecars.
A few things below. The main ones are scope (this PR bundles three unrelated changes) and ~30 lines of duplicated conditional-request handling.
Scope
This PR is doing three independent things:
- Configurable Maven upstream + Gradle Plugin Portal fallback (the stated purpose)
- Prometheus metrics for the Gradle build cache handler (
gradle.go+gradle_test.go) - README drive-by edits (Kotlin DSL fixes in the build cache snippet, blank-line whitespace)
The metrics work is reasonable on its own but unrelated to plugin portal fallback. Could it be split into its own PR?
Design
The README is upfront that fallback is marker-only and implementation jars still come from the primary upstream. That means a plugin published only to the Gradle Plugin Portal will resolve its marker through this proxy and then 404 on the actual jar. Is shipping the half-feature useful as-is, or should fallback cover implementation coordinates before this lands?
| upstreamURL := fmt.Sprintf("%s/%s", h.upstreamURL, urlPath) | ||
|
|
||
| body, contentType, err := h.proxy.FetchOrCacheMetadata(r.Context(), "maven", cacheKey, upstreamURL, "*/*") | ||
| if err != nil && h.shouldFallbackToPluginPortal(urlPath, filename) { |
There was a problem hiding this comment.
This shouldFallbackToPluginPortal check is always true — line 83 already returned early if it was false. Can be just if err != nil {.
| upstreamURL := fmt.Sprintf("%s/%s", h.upstreamURL, urlPath) | ||
|
|
||
| result, err := h.proxy.GetOrFetchArtifactFromURL(r.Context(), "maven", name, version, filename, upstreamURL) | ||
| if err != nil && h.shouldFallbackToPluginPortal(urlPath, filename) { |
There was a problem hiding this comment.
This falls back to the plugin portal on any error, not just 404. A timeout or 5xx from Maven Central will trigger a plugin portal lookup. If that's intentional for resilience it's fine, but the test uses errors.New("404 not found") rather than ErrUpstreamNotFound, which suggests it wasn't a deliberate choice. Same applies to the metadata path above.
| h.writeMetadataResponse(w, r, cacheKey, body, contentType) | ||
| } | ||
|
|
||
| func (h *MavenHandler) writeMetadataResponse(w http.ResponseWriter, r *http.Request, cacheKey string, body []byte, contentType string) { |
There was a problem hiding this comment.
This function is a verbatim copy of ProxyCached lines 683–713 in handler.go (ETag / If-Modified-Since / Warning header handling). The next change to conditional-request handling now has to be made in two places.
Could this be extracted to a shared helper on Proxy, or could ProxyCached grow a fallback-URL variant so this stays in one place?
Also worth noting: this path always buffers via FetchOrCacheMetadata, bypassing the !p.CacheMetadata streaming branch that ProxyCached has. Harmless for tiny marker POMs but it's a behavioural divergence.
| parts := strings.Split(urlPath, "/") | ||
|
|
||
| groupEnd := len(parts) - artifactVersionPathOffset | ||
| artifactIdx := len(parts) - artifactVersionPathOffset |
There was a problem hiding this comment.
nit: groupEnd and artifactIdx are always the same value (the artifact segment immediately follows the group segments). One variable would do.
| // Routes returns the HTTP handler for Gradle HttpBuildCache requests. | ||
| func (h *GradleBuildCacheHandler) Routes() http.Handler { | ||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| start := time.Now() |
There was a problem hiding this comment.
The metrics instrumentation in this file (and the accompanying test) is unrelated to the Maven/plugin-portal change. Would be easier to review as a separate PR.
| w.WriteHeader(http.StatusCreated) | ||
| } | ||
|
|
||
| type statusCapturingResponseWriter struct { |
There was a problem hiding this comment.
FYI an identical wrapper already exists at internal/server/server.go:936 (responseWriter). Different package so not trivially shareable, just flagging the duplication.
| ## Configuration | ||
|
|
||
| The proxy can be configured via: | ||
|
|
There was a problem hiding this comment.
nit: this and the blank line after Requirements: below are unrelated whitespace changes.
| } | ||
| remote<HttpBuildCache> { | ||
| url = uri("http://localhost:8080/gradle/") | ||
| isAllowInsecureProtocol = true // if not using HTTPS |
There was a problem hiding this comment.
The push → isPush / isAllowInsecureProtocol change is a correct Kotlin DSL fix, but it's unrelated to this PR. Worth landing separately so it doesn't get tangled with the plugin-portal discussion.
Adds configurable Maven upstream support and Gradle Plugin Portal fallback for Gradle plugin marker artifacts on the Maven endpoint.
Introduces new upstream config/env options, wires them through server startup, and extends Maven artifact handling to include .module files.
Tests were added/updated to validate fallback and caching behavior for plugin markers, including real-world plugin coordinates.